/*
 * Binder.java
 *
 * Created on November 4, 2006, 8:37 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package edu.umn.cs5115.scheduler.framework.gui;

import edu.umn.cs5115.scheduler.framework.KeyValueAdapter;
import edu.umn.cs5115.scheduler.framework.KeyValueCoding;

/**
 * Binds two KeyValueCoding things together.
 * @author grant
 */
public class Binder extends KeyValueAdapter
{
    private KeyValueCoding firstObject;
    private String firstKey;
    private KeyValueCoding secondObject;
    private String secondKey;
    private ValueTransformer transformer;
    /** Limit the number of recursive bindings to 1 to prevent infinite loops. */
    private int bindingLevel;
    private final static int MAX_BINDING_LEVEL = 1;
    private boolean unbound = false;
    
    /** 
     * Creates a new instance of Binder that will bind these two objects.
     * The second object will lose its current value and take on the value of
     * the first object being bound.
     * @param firstObject The first object to be bound.
     * @param firstKey The key of the value on the first object to be bound.
     * @param secondObject the second object to be bound.
     * @param secondKey The key of the value on the second object to be bound.
     */
    public Binder(KeyValueCoding firstObject, String firstKey, KeyValueCoding secondObject, String secondKey)
    {
        this(firstObject, firstKey, secondObject, secondKey, ValueTransformer.IDENTITY_TRANSFORM);
    }
    
    /** 
     * Creates a new instance of Binder that will bind these two objects.
     * The second object will lose its current value and take on the value of
     * the first object being bound.
     * @param firstObject The first object to be bound.
     * @param firstKey The key of the value on the first object to be bound.
     * @param secondObject the second object to be bound.
     * @param secondKey The key of the value on the second object to be bound.
     * @param transformer The ValueTransformer to use to transform the value
     * from the first object to the second object.  The reverse transformation 
     * will be used to transform from the second object to the first object.
     */
    public Binder(KeyValueCoding firstObject, String firstKey, KeyValueCoding secondObject, String secondKey, ValueTransformer transformer)
    {
        this.firstObject = firstObject;
        this.firstKey = firstKey;
        this.secondObject = secondObject;
        this.secondKey = secondKey;
        this.transformer = transformer;
        secondObject.setValueForKey(secondKey, transformer.transform(firstObject.getValueForKey(firstKey)));
        firstObject.addListener(this, firstKey);
        secondObject.addListener(this, secondKey);
    }
    
    /**
     * Destroys the binding that was created when this Binder was created.
     */
    public void unbind()
    {
        firstObject.removeListener(this, firstKey);
        secondObject.removeListener(this, secondKey);
        unbound = true;
    }

    public void valueChanged(KeyValueCoding object, String key)
    {
        if (!unbound && bindingLevel < MAX_BINDING_LEVEL)
        {
            bindingLevel++;
            if (object == firstObject)
            {
                secondObject.setValueForKey(secondKey, transformer.transform(object.getValueForKey(key)));
                // make sure that data validation gets reflected.
                if (!unbound)
                    firstObject.setValueForKey(firstKey, transformer.reverseTransform(secondObject.getValueForKey(secondKey)));
            } else if (object == secondObject) {
                firstObject.setValueForKey(firstKey, transformer.reverseTransform(object.getValueForKey(key)));
                // make sure that data validation gets reflected
                if (!unbound)
                    secondObject.setValueForKey(secondKey, transformer.transform(firstObject.getValueForKey(firstKey)));
            }
            bindingLevel--;
        }
    }
}
